package com.xflib.tool.gitlab;

import cn.hutool.core.date.SystemClock;
import cn.hutool.core.lang.Assert;
import org.apache.commons.lang3.ObjectUtils;
import org.gitlab4j.api.GitLabApi;
import org.gitlab4j.api.GitLabApiException;
import org.gitlab4j.api.models.Branch;
import org.gitlab4j.api.models.Commit;
import org.gitlab4j.api.models.Project;
import org.gitlab4j.api.models.Tag;

import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;

public class GitlabUtil {
    String gitlabUrl; // http:localhost:8081
    String token; // 需要登录gitlab账户，维护token值，access_token
    GitLabApi gitLabApi;

    public GitLabApi getGitLabApi() {
        return gitLabApi;
    }

    public static GitlabUtil create(String gitlabUrl, String token){
        GitlabUtil gitlabUtil= new GitlabUtil();
        gitlabUtil.token = token;
        gitlabUtil.gitlabUrl=gitlabUrl;
        gitlabUtil.gitLabApi = new GitLabApi(gitlabUtil.gitlabUrl,gitlabUtil.token);
        return gitlabUtil;
    }

    /**
     * 获取最后一个tag之后的commits
     * @param project
     * @param sha
     * @return
     * @throws GitLabApiException
     */
    public List<Commit> getNonTagCommitsByProjectAndSha(Project project, String sha) throws GitLabApiException {
        List<Commit> result = new ArrayList<>();

        AtomicLong date = new AtomicLong(0);
        gitLabApi.getTagsApi().getTags(project,1).stream().findFirst().ifPresent(tag -> date.set(tag.getCommit().getCommittedDate().getTime()));
        result.addAll(gitLabApi.getCommitsApi().getCommits(project, sha, new Date(date.get()), new Date(SystemClock.now())));

        return result;
    }

    /**
     * 根据 项目webUrl路径 获取项目，
     * @param webUrl, 例如：http://192.168.3.179:8091/fdd/hd/AntInSchool
     * @return {@link Project}
     * @throws GitLabApiException
     */
    public Project getProjectByPath(String webUrl) throws GitLabApiException {
        List<Project> result = new ArrayList<>();

        String ProjectName = webUrl.substring(
                (webUrl.endsWith("/")?webUrl.substring(0,webUrl.length()-1):webUrl).lastIndexOf("/")+1
        );
        gitLabApi.getProjectApi().getProjects(ProjectName).forEach(project -> {
            if(project.getWebUrl().endsWith(webUrl)){
                result.add(project);
            }
        });
        if(0 == result.size()) throw new GitLabApiException(String.format("Project [%s] not founded.", webUrl));
        if(1 < result.size()) throw new GitLabApiException(String.format("Multi project [%s] founded.", webUrl));

        return result.get(0);
    }

    /**
     * 为项目指定分支加tag
     * @param project
     * @param branch
     * @param tagName
     * @throws GitLabApiException
     */
    public List<Tag> createTag(Project project, String branch, String tagName) throws GitLabApiException {
        String myTagName=tagName;

        // 如果已存在则先删除
        gitLabApi.getTagsApi().getTags(project).stream().filter(tag->tag.getName().equals(myTagName)).findFirst().ifPresent(tag-> {
            try {
                gitLabApi.getTagsApi().deleteTag(project, myTagName);
            } catch (GitLabApiException e) {
                e.printStackTrace();
            }
        });
        // 创建Tag
        gitLabApi.getTagsApi().createTag(project
                , myTagName//.concat(isSnapShot ? ".".concat(DateUtil.format(new Date(), DatePattern.PURE_DATETIME_PATTERN)) : "")
                ,branch
        );
        return gitLabApi.getTagsApi().getTags(project);

    }

    /**
     * 创建新的分支
     * @param project
     * @param newBranch
     * @param refBranch
     * @return
     */
    public Branch createBranch(Project project, String newBranch, String refBranch) throws Exception {

        // newBranch 不能为空
        Assert.notBlank(newBranch);

        // 默认的引用分支为 master
        if(ObjectUtils.isNotEmpty(refBranch)){ //
            refBranch = "master";
        }

        // 项目仓库中存在 refBranch 并且不存在分支 newBranch 时创建分支
        AtomicBoolean isNonExistNewBranch= new AtomicBoolean(true);
        AtomicBoolean isExistRefBranch= new AtomicBoolean(false);
        String finalRefBranch = refBranch;
        Optional.ofNullable(gitLabApi.getRepositoryApi().getBranches(project.getId())).ifPresent(branches->{
            branches.forEach(branche->{
                if(ObjectUtils.isNotEmpty(newBranch) && branche.getName().equalsIgnoreCase(newBranch)) {
                    isNonExistNewBranch.set(false);
                }
                if(ObjectUtils.isNotEmpty(finalRefBranch) && branche.getName().equalsIgnoreCase(finalRefBranch)) {
                    isExistRefBranch.set(true);
                }
            });
        });

        if(isNonExistNewBranch.get() && isExistRefBranch.get()) {
            return gitLabApi.getRepositoryApi().createBranch(project.getId(), newBranch, refBranch);
        }
        if(!isNonExistNewBranch.get()) {
            throw new BranchHasExistsException("Branch " + newBranch + " has exists.");
        }
        if(!isExistRefBranch.get()) {
            throw new BranchNotFoundException("Branch " + refBranch + " not found.");
        } else {
            throw new Exception("Branch " + newBranch + " has exists or branch " + refBranch + " not found.");
        }
    }

    public Branch createBranch(Project project, String newBranch) throws Exception {
        return createBranch(project,newBranch,"master");
    }
    public Branch createBranch(Project project) throws Exception {
        return createBranch(project,null);
    }

    /**
     * 删除已经合并的分支，返回未合并的分支名称清单
     * @param project
     * @return list of reservedBranches
     */
    public List<Branch> deleteMergedBranches(Project project)
            throws GitLabApiException {
        List<Branch> result = new LinkedList<>();

        gitLabApi.getRepositoryApi().deleteMergedBranches(project.getId());
        Optional.ofNullable(gitLabApi.getRepositoryApi().getBranches(project.getId())).ifPresent(
            branches-> branches.forEach(branche->result.add(branche))
        );

        return result;
    }

    /**
     * 获取  sinceTag 到 untilTag 之间的所有提交信息
     * @param project
     * @param sinceTag
     * @param untilTag
     * @return
     * @throws GitLabApiException
     */
    public List<Commit> getCommitsByTag(Project project, String sinceTag, String untilTag)
            throws GitLabApiException {
        Commit commit1 = gitLabApi.getTagsApi().getTag(project.getId(),sinceTag).getCommit();
        Commit commit2 = gitLabApi.getTagsApi().getTag(project.getId(),untilTag).getCommit();
        List<Commit> commits =  gitLabApi.getCommitsApi().getCommits(project.getId(),"master",commit1.getCreatedAt(),commit2.getCommittedDate());
        return commits;
    }

    /**
     * 获取 从 tag 到 今天 所有提交信息
     * @param project
     * @param tag
     * @return
     * @throws GitLabApiException
     */
    public List<Commit> getCommitsByTag(Project project, String tag)
            throws GitLabApiException {
        Commit commit = gitLabApi.getTagsApi().getTag(project.getId(),tag).getCommit();
        List<Commit> commits =  gitLabApi.getCommitsApi().getCommits(project.getId(),"master",commit.getCreatedAt(),new Date());
        return commits;
    }

    /**
     * 对比两个tag或分支之间的差异
     * 注意：这是一个不完全对比，因为嘉定了 ref2 创建自 ref1，应用场景是：
     * 从master创建tester分支，所有开发分支都是由开发人员自行合并到tester，测试完成后，通知管理员合并tester到master。
     * @param project
     * @param ref1
     * @param ref2
     * @return
     */
    public List<Commit> getDeference(Project project, String ref1, String ref2) throws GitLabApiException {

        Commit commit2 = gitLabApi.getRepositoryApi().getBranch(project.getId(),ref2).getCommit();
        Date beginDate=commit2.getCommittedDate();

        List<Commit> commits1=gitLabApi.getCommitsApi().getCommits(project.getId(),ref1,beginDate,new Date());
        List<Commit> commits2=gitLabApi.getCommitsApi().getCommits(project.getId(),ref2,beginDate,new Date());

        for (Commit commit : commits1) {
            commits2.removeIf(t->t.getId().equals(commit.getId()));
        }

        return commits2;

    }


    public static void main(String[] args) throws GitLabApiException {

        // 获取 最新提交信息 (仅title，没有分析message)
        GitlabUtil gitlabUtil = GitlabUtil.create("http://192.168.3.179:8091","2askQvp98z8cBR5fLZe1");
        Project project = gitlabUtil.getProjectByPath("/fdd/hd/AntInSchool");

        Set<String> titles = new LinkedHashSet<>();
        gitlabUtil.getNonTagCommitsByProjectAndSha(project,"master")
                .forEach(commit -> {
                    if(!commit.getMessage().startsWith("Merge ")) {
                        titles.add(commit.getTitle());
                    }
                } );
        titles.forEach(t->System.out.println(t));

        // 加tag
        gitlabUtil.createTag(project,"tester1.21","tag_test2");


    }

}
